package cn.com.tarotframework.utils;

import cn.com.tarotframework.common.Comments;
import cn.com.tarotframework.server.workflow.eu.FlowStatus;
import cn.com.tarotframework.server.workflow.po.FlowLine;
import cn.com.tarotframework.server.workflow.po.FlowNode;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class FlowNodeUtil {

    /**
     * 在当前集合中，根据当前集合获取父节点集合
     * 将当前结合与父节点集合相比，判断父节点为：EXECUTE_WAIT的对应的 子节点集合的 节点集合
     * @param flowNodes 原节点集合
     * @param flowLines 原节点连线集合
     * @param currentFlowNodes 当前节点集合
     * @return List<FlowNode>
     */
    public static List<FlowNode> searchCurrentListChildFlowNodes(List<FlowNode> flowNodes, List<FlowLine> flowLines, List<FlowNode> currentFlowNodes) {
        return currentFlowNodes.stream()
                .filter(node -> adjacentFlowNode(flowNodes, flowLines, node, Comments.EXECUTE_BEFORE_NODE).stream().anyMatch(parent -> parent.getStatus() == FlowStatus.EXECUTE_WAIT))
                .collect(Collectors.toList());
    }

    /**
     * 从当前执行节点集合中找出父节点
     * @param currentFlowNodeList 当前执行节点集合
     * @return List<FlowNode>
     */
    public static List<FlowNode> searchParentFlowNodes(List<FlowNode> flowNodes, List<FlowLine> flowLines, List<FlowNode> currentFlowNodeList) {
        return currentFlowNodeList.stream().flatMap(
                current -> adjacentFlowNode(flowNodes, flowLines, current, Comments.EXECUTE_BEFORE_NODE).stream().filter( node -> node.getStatus() == FlowStatus.EXECUTE_WAIT)
        ).collect(Collectors.toList());
    }

    /**
     * 从当前节点集合中，找出子节点
     * @param flowNodes 原节点集合
     * @param flowLines 原节点连线集合
     * @param currentFlowNodeList 当前执行节点结合
     * @param parentNodes 当前执行节点集合中的父节点集合
     * @return List<FlowNode>
     */
    public static List<FlowNode> searchChildFlowNodes(List<FlowNode> flowNodes, List<FlowLine> flowLines, List<FlowNode> currentFlowNodeList, List<FlowNode> parentNodes) {
        return parentNodes.stream().flatMap(
                temporary -> adjacentFlowNode(flowNodes, flowLines, temporary, Comments.EXECUTE_AFTER_NODE).stream().flatMap(
                        afterNode -> currentFlowNodeList.stream().filter(parallelNode -> parallelNode.getId().equals(afterNode.getId())))
        ).distinct().collect(Collectors.toList());
    }

    /**
     * 从整个集合中，获取节点集合
     * @param flowNodes 原节点集合
     * @param flowLines 原节点连线集合
     * @param flowNode 当前节点信息
     * @param direction 节点标识 【0：查询当前节点的前面节点，1： 查询当前节点的后面节点】
     * @return List<FlowNode>
     */
    public static List<FlowNode> adjacentFlowNode(List<FlowNode> flowNodes, List<FlowLine> flowLines, FlowNode flowNode, int direction) {
        return flowLines.stream()
                .filter(line -> Objects.equals( direction == Comments.EXECUTE_BEFORE_NODE ? line.getTarget() : line.getSource(), flowNode.getId())).collect(Collectors.toList())
                .stream()
                .flatMap(line -> flowNodes.stream().filter(node -> Objects.equals(direction == Comments.EXECUTE_BEFORE_NODE ? line.getSource() : line.getTarget(), node.getId())))
                .sorted(Comparator.comparing(FlowNode::getParallel)).collect(Collectors.toList());
    }

    /**
     * 从整个集合中，获取节点集合
     * @param flowNodes 原节点集合
     * @param flowLines 原节点连线集合
     * @param flowNodeList 当前节点集合信息
     * @param direction 节点标识 【0：查询当前节点的前面节点，1： 查询当前节点的后面节点】
     * @return List<FlowNode>
     */
    public static List<FlowNode> adjacentFlowNode(List<FlowNode> flowNodes, List<FlowLine> flowLines, List<FlowNode> flowNodeList, int direction) {
        return flowNodeList.stream()
                .flatMap(flowNode -> flowLines.stream().filter(line -> Objects.equals( direction == Comments.EXECUTE_BEFORE_NODE ? line.getTarget() : line.getSource(), flowNode.getId())))
                .flatMap(line -> flowNodes.stream().filter(node -> Objects.equals(direction == Comments.EXECUTE_BEFORE_NODE ? line.getSource() : line.getTarget(), node.getId())))
                .distinct().collect(Collectors.toList());
    }

    /**
     * 集合合并，去重，然后从小到大排序
     * @param merge 流程业务对象
     * @return FlowNodeAssemble<FlowNode>
     */
    @SafeVarargs
    public static List<FlowNode> mergeFlowNodes(List<FlowNode> ...merge) {
       return Arrays.stream(merge).flatMap(Collection::stream).distinct().sorted(Comparator.comparing(FlowNode::getParallel)).collect(Collectors.toList());
    }

    /**
     * 合并节点集合：将待执行节点集合，即将要执行的节点进行合并，提取其中的所有子节点
     * @param flowNodes 原节点集合
     * @param flowLines 原节点连线集合
     * @param merge 要合并的集合
     * @return FlowNodeAssemble<FlowNode>
     */
    @SafeVarargs
    public static List<FlowNode> mergeFlowNodes(List<FlowNode> flowNodes, List<FlowLine> flowLines, List<FlowNode> ...merge) {
        List<FlowNode> mergeFlowNodes = Arrays.stream(merge)
                .flatMap(Collection::stream).sorted(Comparator.comparing(FlowNode::getParallel)).distinct().collect(Collectors.toList());
        List<FlowNode> parentFlowNodes = mergeFlowNodes.stream().flatMap(
                current -> FlowNodeUtil.adjacentFlowNode(flowNodes, flowLines, current, Comments.EXECUTE_BEFORE_NODE).stream()
        ).filter(node -> node.getStatus() != FlowStatus.EXECUTE_SUCCESS).distinct().collect(Collectors.toList());
        return FlowNodeUtil.cleanExecuteNode(mergeFlowNodes, parentFlowNodes);
    }

    /**
     * 删除：删除指定集合中，指定的子节点集合
     * @param flowNodes 将要执行的节点集合
     * @param deleteNodes 要删除的节点集合
     * @return List<FlowNode>
     */
    public static List<FlowNode> cleanExecuteNode(List<FlowNode> flowNodes, List<FlowNode> deleteNodes) {
        deleteNodes.forEach( node -> IntStream.range(0, flowNodes.size()).filter( i -> flowNodes.get(i).getId().equals(node.getId()))
                .boxed().findFirst().map( i -> flowNodes.remove((int)i))
        );
        return flowNodes;
    }

    /**
     * 删除：删除指定集合中，指定的子节点集合
     * @param flowNodes 将要执行的节点集合
     * @param deleteNode 要删除的节点
     * @return List<FlowNode>
     */
    public static List<FlowNode> cleanExecuteNode(List<FlowNode> flowNodes, FlowNode deleteNode) {
        IntStream.range(0, flowNodes.size()).filter( i -> flowNodes.get(i).getId().equals(deleteNode.getId()))
                .boxed().findFirst().map( i -> flowNodes.remove((int)i));
        return flowNodes;
    }
}
